--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 2730af93a08b5a8d70f000e989c08a13bb11e8b9
Parents : 49e403c
Author : Mark Qvist <bc7291552be7a58f361522990465165c>
Signature : T66BB85Valid, signed by author
Date : 2026-06-07T13:36:12+02:00
Added FilePlayer release on finish functionality
Changes
6 files changed, 73 insertions(+), 20 deletions(-)
Diff
diff --git a/LXST/Pipeline.py b/LXST/Pipeline.py
index 6fd377b..af63233 100644
--- a/LXST/Pipeline.py
+++ b/LXST/Pipeline.py
@@ -1,3 +1,5 @@
+import threading
+
from .Sources import *
from .Sinks import *
from .Codecs import *
@@ -8,6 +10,8 @@ class PipelineError(Exception):
pass
class Pipeline():
+ release_lock = threading.Lock()
+
def __init__(self, source, codec, sink, processor = None):
if not issubclass(type(source), Source): raise PipelineError("Audio pipeline initialised with invalid source")
if not issubclass(type(sink), Sink) : raise PipelineError("Audio pipeline initialised with invalid sink")
@@ -23,12 +27,18 @@ class Pipeline():
if isinstance(sink, Packetizer): sink.source = source
if isinstance(sink, OpusFileSink): sink.source = source
+ def release(self):
+ if self.release_lock.locked(): return
+ with self.release_lock:
+ if not hasattr(self, "released") or not self.released:
+ self.released = True
+ self.source = None
+ self._codec = None
+
@property
def codec(self):
- if self.source:
- return self.source.codec
- else:
- return None
+ if self.source: return self.source.codec
+ else: return None
@codec.setter
def codec(self, codec):
@@ -40,19 +50,15 @@ class Pipeline():
@property
def sink(self):
- if self.source:
- return self.source.sink
- else:
- return None
+ if self.source: return self.source.sink
+ else: return None
@property
def running(self):
return self.source.should_run
def start(self):
- if not self.running:
- self.source.start()
+ if not self.running: self.source.start()
def stop(self):
- if self.running:
- self.source.stop()
\ No newline at end of file
+ if self.running: self.source.stop()
\ No newline at end of file
diff --git a/LXST/Primitives/Players.py b/LXST/Primitives/Players.py
index 381b7da..e487eee 100644
--- a/LXST/Primitives/Players.py
+++ b/LXST/Primitives/Players.py
@@ -2,15 +2,17 @@ import LXST
import time
import threading
import os
+import gc
from LXST.Sinks import LineSink
from LXST.Sources import OpusFileSource
class FilePlayer():
- def __init__(self, path=None, device=None, loop=False):
+ def __init__(self, path=None, device=None, loop=False, release_on_finish=False):
self._file_path = path
self._playback_device = None
self.__finished_callback = None
+ self.__release_on_finish = release_on_finish
self.__loop = loop
self.__source = None
self.__sink = LineSink(self._playback_device)
@@ -23,11 +25,19 @@ class FilePlayer():
@property
def running(self):
if not self.__source: return False
- else: return self.__source.should_run
+ else: return self.__source.should_run or len(self.__sink.frame_deque)
@property
def playing(self): return self.running
+ @property
+ def release_on_finish(self): return self.__release_on_finish
+
+ @release_on_finish.setter
+ def release_on_finish(self, value):
+ if type(value) != bool: raise TypeError("Provided value is not bool")
+ else: self.__release_on_finish = value
+
@property
def finished_callback(self): return self.__finished_callback
@@ -38,10 +48,11 @@ class FilePlayer():
else: self.__finished_callback = callback
def __callback_job(self):
- if self.__finished_callback:
+ if self.__finished_callback or self.__release_on_finish:
time.sleep(0.2)
while self.running: time.sleep(0.1)
- self.__finished_callback(self)
+ if self.__finished_callback: self.__finished_callback(self)
+ if self.__release_on_finish: self.release()
def set_source(self, path=None):
if not path: return
@@ -60,7 +71,7 @@ class FilePlayer():
if not self.running and self.__source:
self.__input_pipeline.start()
self.__output_pipeline.start()
- if self.__finished_callback:
+ if self.__finished_callback or self.__release_on_finish:
threading.Thread(target=self.__callback_job, daemon=True).start()
def stop(self):
@@ -68,4 +79,15 @@ class FilePlayer():
self.__input_pipeline.stop()
self.__output_pipeline.stop()
- def play(self): self.start()
\ No newline at end of file
+ def play(self): self.start()
+
+ def release(self):
+ self.stop()
+ self.__source.release()
+ self.__input_pipeline.release()
+ self.__output_pipeline.release()
+ self.__input_pipeline = None
+ self.__output_pipeline = None
+ self.__sink = None
+ self.__source = None
+ self.__loopback = None
\ No newline at end of file
diff --git a/LXST/Sinks.py b/LXST/Sinks.py
index 06bd364..3d51c38 100644
--- a/LXST/Sinks.py
+++ b/LXST/Sinks.py
@@ -109,6 +109,16 @@ def get_backend():
Backend = get_backend()
class Sink():
+ release_lock = threading.Lock()
+ def release(self):
+ if self.release_lock.locked(): return
+ with self.release_lock:
+ if not hasattr(self, "released") or not self.released:
+ self.released = True
+ self.should_run = False
+ self.frame_deque = None
+ self.backend = None
+
def handle_frame(self, frame, source): pass
def can_receive(self, from_source=None): return True
diff --git a/LXST/Sources.py b/LXST/Sources.py
index 1312d3b..04ef848 100644
--- a/LXST/Sources.py
+++ b/LXST/Sources.py
@@ -118,7 +118,21 @@ def get_backend():
Backend = get_backend()
-class Source(): pass
+class Source():
+ release_lock = threading.Lock()
+ def release(self):
+ if self.release_lock.locked(): return
+ with self.release_lock:
+ if not hasattr(self, "released") or not self.released:
+ self.released = True
+ self.should_run = False
+ self.frame_deque = None
+ self.codec = None
+ self._sink = None
+ self._source = None
+ self._filters = None
+ self.backend = None
+
class LocalSource(Source): pass
class RemoteSource(Source): pass
diff --git a/LXST/_version.py b/LXST/_version.py
index 3dd3d2d..a34b2f6 100644
--- a/LXST/_version.py
+++ b/LXST/_version.py
@@ -1 +1 @@
-__version__ = "0.4.6"
+__version__ = "0.4.7"
diff --git a/examples/fileplayer.py b/examples/fileplayer.py
index c0724f9..be493f3 100644
--- a/examples/fileplayer.py
+++ b/examples/fileplayer.py
@@ -15,6 +15,7 @@ if loop:
else:
player = FilePlayer("./docs/speech_stereo.opus")
+ player.release_on_finish = True
player.start()
while player.running: time.sleep(0.1)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────